using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Gko.Utils.Interfaces;

namespace Gko.Utils
{
	public class Background : IBackground
	{
		private readonly SynchronizationContext context;

		public Background(SynchronizationContext context)
		{
			this.context = context;
		}

		public static void Invoke(Action action)
		{
			Invoke(action, null);
		}

		public static void Invoke<T>(Action<T> action, T arg)
		{
			Invoke(action, arg, null);
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "2"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "1")]
		public static void Invoke<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2)
		{
			Invoke(action.Partial(arg1), arg2, null);
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "3"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "2"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "1")]
		public static void Invoke<T1, T2, T3>(Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3)
		{
			Invoke(action.Partial(arg1), arg2, arg3);
		}

		public static void Invoke(Action action, Action<Exception> exceptionCallback)
		{
			ThreadPool.QueueUserWorkItem(p => BackgroundInvoke(action, exceptionCallback));
		}

		public static void Invoke<T>(Action<T> action, T arg, Action<Exception> exceptionCallback)
		{
			Invoke(action.Partial(arg), exceptionCallback);
		}

		#region IBackground Members

		public void Run(Action action)
		{
			Invoke(this.BackgroundRun, action);
		}

		public void Run<T>(Action<T> action, T arg)
		{
			this.Run(action.Partial(arg));
		}

		public void Run(Action action, Action callback)
		{
			ThreadPool.QueueUserWorkItem(p => this.BackgroundRun(action, callback));
		}

		public void Run<T>(Action<T> action, Action callback, T arg)
		{
			this.Run(action.Partial(arg), callback);
		}

		public void Run<T>(Action<T> action, Action<T> callback, T arg)
		{
			this.Run(action.Partial(arg), callback.Partial(arg));
		}

		public void Run<T>(Func<T> func, Action<T> callback)
		{
			ThreadPool.QueueUserWorkItem(p => this.BackgroundRun(func, callback));
		}

		public void Run<T, TResult>(Func<T, TResult> func, Action<TResult> callback, T arg)
		{
			this.Run(func.Partial(arg), callback);
		}

		public void Run<T, TResult>(Func<T, TResult> func, Action<T, TResult> callback, T arg)
		{
			this.Run(func.Partial(arg), callback.Partial(arg));
		}

		public event EventHandler<UnhandledExceptionEventArgs> UnhandledException;

		public IBackgroundTransferObservable Transfer<T>(IEnumerable<T> source, IList<T> target)
		{
			return new BackgroundTransferObservable<T>(this.context, source, target);
		}

		#endregion

		private static void BackgroundInvoke(Action action, Action<Exception> exceptionCallback)
		{
			try {
				action();
			} catch (Exception e) {
				if (exceptionCallback == null) {
					throw;
				}
				exceptionCallback(e);
			}
		}

		private void BackgroundRun(Action action)
		{
			BackgroundInvoke(action, this.OnException);
		}

		private void BackgroundRun(Action action, Action callback)
		{
			BackgroundInvoke(action.Union(this.context.BeginInvoke, callback), this.OnException);
		}

		private void BackgroundRun<T>(Func<T> func, Action<T> callback)
		{
			BackgroundInvoke(func.Union(this.context.BeginInvoke, callback), this.OnException);
		}

		private void OnException(Exception e)
		{
			this.UnhandledException.Raise(this, new UnhandledExceptionEventArgs(e, false));
		}
	}
}